Skip to content

Conversation

@joaquim-verges
Copy link
Member

@joaquim-verges joaquim-verges commented Jan 12, 2026


PR-Codex overview

This PR focuses on updating the thirdweb payment protocol to support version 2 (x402 v2), enhancing header handling for payment requests and responses, and improving the overall payment processing flow.

Detailed summary

  • Updated x402Version to support both 1 and 2.
  • Changed payment header retrieval to prioritize PAYMENT-SIGNATURE over X-PAYMENT.
  • Introduced new functions in headers.ts for handling payment request and response headers.
  • Modified multiple files to reflect changes in payment data handling and processing.
  • Enhanced error handling to provide version-specific responses.
  • Updated tests to ensure compatibility with new header and version logic.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Support for X.402 v2 with header-based payment responses and dynamic request/response header selection.
  • Improvements

    • Expanded payment configuration (price, minPrice, facilitator, routing, extra metadata, optional protocol version).
    • Smarter version handling: preserves client-provided version, falls back sensibly, and formats 402 responses per negotiated version.
    • Prefer PAYMENT-SIGNATURE header with fallback to older header.
  • Tests

    • Updated tests to validate dynamic header behavior.
  • Chores

    • Added changelog entry for v2 support.

✏️ Tip: You can customize this high-level summary in your review settings.

@joaquim-verges joaquim-verges requested review from a team as code owners January 12, 2026 20:46
@vercel
Copy link

vercel bot commented Jan 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
docs-v2 Ready Ready Preview, Comment Jan 13, 2026 7:29pm
nebula Ready Ready Preview, Comment Jan 13, 2026 7:29pm
thirdweb_playground Ready Ready Preview, Comment Jan 13, 2026 7:29pm
thirdweb-www Ready Ready Preview, Comment Jan 13, 2026 7:29pm
wallet-ui Ready Ready Preview, Comment Jan 13, 2026 7:29pm

@changeset-bot
Copy link

changeset-bot bot commented Jan 12, 2026

🦋 Changeset detected

Latest commit: f458196

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
thirdweb Patch
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch
wagmi-inapp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added packages SDK Involves changes to the thirdweb SDK labels Jan 12, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

Walkthrough

Adds x402 v2 support: introduces versioned header constants and helpers, a v2 header encoding function, schema/type updates to accept v2, and makes header selection and x402Version handling dynamic across decode, fetch, verify, and settle flows.

Changes

Cohort / File(s) Summary
Header Management
packages/thirdweb/src/x402/headers.ts
New module exporting v1/v2 header constants and helpers getPaymentRequestHeader() / getPaymentResponseHeader() that resolve header names by version.
Encoding
packages/thirdweb/src/x402/encode.ts
Added encodePaymentRequired to produce base64 PAYMENT-REQUIRED header payloads (x402 v2).
Common Payment Logic
packages/thirdweb/src/x402/common.ts
Added internal helpers to format v1 (body) and v2 (header) payment-required responses; decodePaymentRequest uses resourceUrl, preserves client-provided x402Version via nullish coalescing, and returns v2-formatted responses when appropriate.
Type System & Versioning
packages/thirdweb/src/x402/types.ts
Added supportedX402Versions, X402Version type, default x402Version = 2; expanded PaymentArgs fields and introduced PaymentRequiredResultV1 / PaymentRequiredResultV2 and union result type.
Schema Validation
packages/thirdweb/src/x402/schemas.ts
FacilitatorSupportedResponseSchema now accepts x402Version as `1
Facilitator API
packages/thirdweb/src/x402/facilitator.ts
accepts now posts x402Version and its return type narrowed to PaymentRequiredResultV1; request/response typing adjusted.
Fetch Flow & Tests
packages/thirdweb/src/x402/fetchWithPayment.ts, packages/thirdweb/src/x402/fetchWithPayment.test.ts
Fetch flow now resolves x402Version (parsed or default) and uses getPaymentRequestHeader() / getPaymentResponseHeader() for dynamic header names; tests updated to assert computed header names.
Settle Payment
packages/thirdweb/src/x402/settle-payment.ts
Uses getPaymentResponseHeader() to populate response headers based on decoded x402Version; response bodies set x402Version via decodedPayment.x402Version ?? x402Version.
Verify Payment
packages/thirdweb/src/x402/verify-payment.ts
Reads payment data from PAYMENT-SIGNATURE header with fallback to X-PAYMENT; response bodies use decodedPayment.x402Version ?? x402Version.
Docs & Examples
apps/*, packages/nexus/*, apps/portal/*
Multiple examples and docs updated to read PAYMENT-SIGNATURE (fallback X-PAYMENT) instead of x-payment.
Changelog
.changeset/eight-pants-drum.md
Adds changeset noting "Support x402 v2."

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Server
  participant Facilitator

  Client->>Server: Initial request (no payment)
  Server->>Client: 402 + payment header (name from getPaymentRequestHeader(x402Version))
  Client->>Facilitator: Payment attempt (sends PAYMENT-SIGNATURE / X-PAYMENT header per version)
  Facilitator->>Server: Settlement call (includes payment data + x402Version)
  Server->>Server: decodePaymentRequest (preserve client x402Version if present)
  Server->>Client: Accept original request retry + payment response header (name from getPaymentResponseHeader)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description is partially filled with PR-Codex generated content but lacks critical information required by the template such as the Linear issue tag and explicit sections for 'Notes for the reviewer' and 'How to test'. Add the Linear issue tag (TEAM-0000 format) at the top, provide explicit 'Notes for the reviewer' section highlighting version compatibility concerns, and add a 'How to test' section documenting test procedures.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'support x402 v2 headers' accurately and concisely summarizes the main objective of the PR, which is adding support for x402 protocol version 2 with enhanced header handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/thirdweb/src/x402/headers.ts (1)

8-10: Consider stricter version validation to avoid silent fallback.

The resolveVersion function silently converts any non-1 value (including 0, 3, or other invalid numbers) to version 2. While the type signature accepts number | X402Version, invalid inputs are masked rather than caught.

Consider throwing for unsupported versions or validating against supportedX402Versions:

♻️ Suggested stricter validation
+import { type X402Version, x402Version, supportedX402Versions } from "./types.js";
+
 function resolveVersion(version?: number | X402Version): X402Version {
-  return version === 1 ? 1 : 2;
+  if (version === undefined) {
+    return x402Version;
+  }
+  if (!supportedX402Versions.includes(version as X402Version)) {
+    throw new Error(`Unsupported x402 version: ${version}`);
+  }
+  return version as X402Version;
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5766c90 and 5b83ba2.

📒 Files selected for processing (8)
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/verify-payment.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Place tests alongside code: foo.tsfoo.test.ts in the same directory
Use real function invocations with stub data in tests; avoid brittle mocks
Use Mock Service Worker (MSW) for fetch/HTTP call interception in tests
Keep tests deterministic and side-effect free
Use predefined test accounts from test/src/test-wallets.ts in tests
Use FORKED_ETHEREUM_CHAIN for mainnet interactions and ANVIL_CHAIN for isolated tests

**/*.test.{ts,tsx}: Co-locate tests with source files using the pattern foo.ts ↔ foo.test.ts
Use real function invocations with stub data in tests; avoid brittle mocks
For network interactions in tests, use Mock Service Worker (MSW) to intercept fetch/HTTP calls, mocking only scenarios that are hard to reproduce
Keep tests deterministic and side-effect free; Vitest is pre-configured

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.test.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/headers.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/verify-payment.ts
🧬 Code graph analysis (6)
packages/thirdweb/src/x402/fetchWithPayment.test.ts (1)
packages/thirdweb/src/x402/headers.ts (1)
  • getPaymentRequestHeader (12-17)
packages/thirdweb/src/x402/common.ts (1)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/fetchWithPayment.ts (2)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/headers.ts (2)
  • getPaymentRequestHeader (12-17)
  • getPaymentResponseHeader (19-26)
packages/thirdweb/src/x402/headers.ts (1)
packages/thirdweb/src/x402/types.ts (2)
  • X402Version (15-15)
  • x402Version (16-16)
packages/thirdweb/src/x402/settle-payment.ts (2)
packages/thirdweb/src/x402/headers.ts (1)
  • getPaymentResponseHeader (19-26)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/verify-payment.ts (1)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Unit Tests
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (17)
packages/thirdweb/src/x402/headers.ts (1)

12-26: LGTM!

The header resolution functions are well-structured with clear return types. The default fallback to x402Version when no version is provided ensures backward compatibility.

packages/thirdweb/src/x402/types.ts (2)

14-16: LGTM!

The versioning implementation using as const with a derived type provides type safety while keeping the supported versions maintainable. The default version set to 2 aligns with the PR objective of supporting x402 v2 headers.


28-31: LGTM!

The JSDoc update accurately documents both header names (PAYMENT-SIGNATURE for v2, X-PAYMENT for v1), which improves discoverability for developers.

packages/thirdweb/src/x402/common.ts (1)

54-55: LGTM!

The nullish coalescing assignment (??=) correctly preserves the client-provided version while defaulting to the current protocol version when missing. This is essential for proper multi-version support where clients may send either v1 or v2 payments.

packages/thirdweb/src/x402/fetchWithPayment.test.ts (3)

4-4: LGTM!

Good addition of the getPaymentRequestHeader import to enable dynamic header validation in tests.


113-117: LGTM!

The assertion now properly validates that the payment header name is dynamically determined based on the x402Version from the mock response. This ensures the implementation correctly handles version-specific headers.


307-311: Good backward compatibility test coverage.

This test validates that v1 payloads (with x402Version: 1 in the base64-encoded header) correctly use the v1 header name (X-PAYMENT). This ensures backward compatibility with existing v1 servers.

packages/thirdweb/src/x402/verify-payment.ts (2)

32-34: LGTM!

The JSDoc example correctly demonstrates the header fallback pattern (v2 payment-signature first, then v1 x-payment), providing clear guidance for implementers.


126-126: LGTM!

Using decodedPayment.x402Version ?? x402Version in error responses ensures the 402 response uses the same protocol version as the client's request. This maintains consistency and allows clients to properly interpret error responses.

Also applies to: 142-142

packages/thirdweb/src/x402/schemas.ts (1)

85-85: LGTM!

The schema correctly expands to accept both X402 protocol versions using a union of literals, maintaining strict type validation while enabling v2 support.

packages/thirdweb/src/x402/settle-payment.ts (4)

4-4: LGTM!

Import correctly added for dynamic header name resolution.


41-43: LGTM!

Example code correctly demonstrates reading payment data from both v2 (payment-signature) and v1 (x-payment) headers with appropriate fallback for backward compatibility.

Also applies to: 110-111


158-173: LGTM!

The dynamic header name resolution correctly uses the client's provided x402Version from the decoded payment, ensuring the response header matches the version the client used in their request.


184-184: Verify the default version fallback behavior in error responses.

The nullish coalescing decodedPayment.x402Version ?? x402Version falls back to the default version (v2) when x402Version is missing from the decoded payment. This means error responses may advertise v2 even if the client originally used v1 but didn't include the version field.

If this is intentional (to encourage clients to upgrade), the current implementation is fine. Otherwise, consider preserving the version from the original 402 response requirements if available.

Also applies to: 200-200

packages/thirdweb/src/x402/fetchWithPayment.ts (3)

8-11: LGTM!

Clean imports with appropriate aliasing. Renaming to defaultX402Version clearly distinguishes the module-level default from the locally-scoped x402Version variable parsed from responses.

Also applies to: 20-20


104-104: LGTM!

Fallback to defaultX402Version ensures backward compatibility with servers that don't include x402Version in their 402 responses. This correctly defaults to v2 for such cases.

Also applies to: 122-122


188-202: LGTM!

Dynamic header name resolution correctly uses the version from the server's 402 response, ensuring the payment request and expected response headers match the protocol version the server indicated.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 12, 2026

size-limit report 📦

Path Size
@thirdweb-dev/nexus (esm) 105.66 KB (0%)
@thirdweb-dev/nexus (cjs) 319.47 KB (0%)

@codecov
Copy link

codecov bot commented Jan 12, 2026

Codecov Report

❌ Patch coverage is 81.08108% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.97%. Comparing base (5766c90) to head (f458196).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
packages/thirdweb/src/x402/encode.ts 25.00% 3 Missing ⚠️
packages/thirdweb/src/x402/fetchWithPayment.ts 75.00% 2 Missing ⚠️
packages/thirdweb/src/x402/headers.ts 90.90% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8623      +/-   ##
==========================================
- Coverage   53.00%   52.97%   -0.04%     
==========================================
  Files         929      932       +3     
  Lines       62109    62270     +161     
  Branches     4077     4095      +18     
==========================================
+ Hits        32921    32987      +66     
- Misses      29089    29184      +95     
  Partials       99       99              
Flag Coverage Δ
packages 52.97% <81.08%> (-0.04%) ⬇️
Files with missing lines Coverage Δ
packages/thirdweb/src/x402/schemas.ts 63.41% <100.00%> (ø)
packages/thirdweb/src/x402/types.ts 100.00% <100.00%> (ø)
packages/thirdweb/src/x402/fetchWithPayment.ts 63.94% <75.00%> (-0.40%) ⬇️
packages/thirdweb/src/x402/headers.ts 90.90% <90.90%> (ø)
packages/thirdweb/src/x402/encode.ts 31.25% <25.00%> (-0.57%) ⬇️

... and 12 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
.changeset/eight-pants-drum.md (1)

1-5: Consider a more descriptive changeset message.

The current description "Support x402 v2" is minimal. Consider expanding it to briefly explain what x402 v2 support includes (e.g., "Add support for x402 v2 protocol with backward compatibility for v1, including versioned headers and dynamic header selection").

Additionally, verify whether this should be a minor version bump rather than patch, as adding support for a new protocol version typically constitutes a feature addition rather than a bug fix.

📝 More descriptive changeset example
 ---
 "thirdweb": patch
 ---
 
-Support x402 v2
+Add x402 v2 protocol support with backward compatibility for v1, including versioned headers and dynamic version detection
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fee1245 and 845889c.

📒 Files selected for processing (1)
  • .changeset/eight-pants-drum.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)

@github-actions github-actions bot added Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase. labels Jan 13, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @apps/portal/src/app/x402/server/page.mdx:
- Line 214: Update the Express and Hono examples to use the v2-first,
v1-fallback header pattern like the page code does: replace the single v1 header
usage with a fallback expression that checks "payment-signature" first then
"x-payment" (apply this to the Express example where req.headers is accessed and
to the Hono example where c.req.header(...) is used), ensuring the paymentData
assignment mirrors the behavior of the const paymentData =
request.headers.get("PAYMENT-SIGNATURE") || request.headers.get("X-PAYMENT")
line.
- Line 386: Update the Express and Hono middleware examples so they read the
same two headers as the Next.js example: change the Express case where
paymentData is sourced from req.headers["x-payment"] to check both
req.headers["payment-signature"] and req.headers["x-payment"], and change the
Hono example where paymentData uses c.req.header("x-payment") to check both
c.req.header("payment-signature") and c.req.header("x-payment"); locate the
occurrences by searching for the paymentData variable and the uses of
req.headers and c.req.header in the middleware examples and apply the same
header precedence as the Next.js snippet.
🧹 Nitpick comments (2)
packages/thirdweb/src/x402/encode.ts (1)

50-63: Consider extracting the inline type for reusability.

The function implementation is correct and follows existing patterns. However, the inline parameter type is moderately complex. If this shape is used elsewhere (e.g., in common.ts for v2 response formatting), consider extracting it to a shared type alias in types.ts for consistency and reuse.

♻️ Optional: Extract type alias
// In types.ts or schemas.ts
export type PaymentRequiredPayload = {
  x402Version: number;
  error?: string;
  accepts: RequestedPaymentRequirements[];
  resource?: { url: string; description?: string; mimeType?: string };
};

Then update the function signature:

-export function encodePaymentRequired(paymentRequired: {
-  x402Version: number;
-  error?: string;
-  accepts: RequestedPaymentRequirements[];
-  resource?: { url: string; description?: string; mimeType?: string };
-}): string {
+export function encodePaymentRequired(
+  paymentRequired: PaymentRequiredPayload,
+): string {
packages/thirdweb/src/x402/types.ts (1)

93-98: Consider adding a discriminant field for easier type narrowing.

The union type works but consumers might benefit from a discriminant. Currently, narrowing requires checking responseBody structure. A version discriminant could simplify this.

One approach would be to add a version discriminant field:

type PaymentRequiredResultV1 = {
  version: 1;
  // ... existing fields
};

type PaymentRequiredResultV2 = {
  version: 2;
  // ... existing fields
};

This would allow cleaner narrowing:

if (result.version === 1) {
  // TypeScript knows this is V1
}

However, this may conflict with existing API contracts, so the current approach is acceptable if discriminant-based narrowing isn't needed by consumers.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 845889c and f458196.

📒 Files selected for processing (12)
  • apps/playground-web/src/app/api/paywall/route.ts
  • apps/portal/src/app/x402/facilitator/page.mdx
  • apps/portal/src/app/x402/page.mdx
  • apps/portal/src/app/x402/server/page.mdx
  • packages/nexus/src/settle-payment.ts
  • packages/nexus/src/verify-payment.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/encode.ts
  • packages/thirdweb/src/x402/facilitator.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/types.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/nexus/src/settle-payment.ts
  • packages/nexus/src/verify-payment.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • apps/playground-web/src/app/api/paywall/route.ts
  • packages/thirdweb/src/x402/encode.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/facilitator.ts
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/playground-web/src/app/api/paywall/route.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • apps/playground-web/src/app/api/paywall/route.ts
  • packages/thirdweb/src/x402/encode.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/facilitator.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • apps/playground-web/src/app/api/paywall/route.ts
  • packages/thirdweb/src/x402/encode.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/facilitator.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/x402/encode.ts
  • packages/thirdweb/src/x402/types.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/settle-payment.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/facilitator.ts
🧬 Code graph analysis (5)
packages/thirdweb/src/x402/encode.ts (1)
packages/thirdweb/src/x402/schemas.ts (1)
  • RequestedPaymentRequirements (40-42)
packages/thirdweb/src/x402/types.ts (2)
packages/thirdweb/src/exports/x402.ts (2)
  • PaymentArgs (18-18)
  • WaitUntil (12-12)
packages/thirdweb/src/x402/facilitator.ts (1)
  • WaitUntil (19-19)
packages/thirdweb/src/x402/fetchWithPayment.ts (2)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/headers.ts (2)
  • getPaymentRequestHeader (12-17)
  • getPaymentResponseHeader (19-26)
packages/thirdweb/src/x402/settle-payment.ts (3)
packages/thirdweb/src/x402/headers.ts (1)
  • getPaymentResponseHeader (19-26)
packages/thirdweb/src/x402/encode.ts (1)
  • safeBase64Encode (71-79)
packages/thirdweb/src/x402/types.ts (1)
  • x402Version (16-16)
packages/thirdweb/src/x402/facilitator.ts (1)
packages/thirdweb/src/x402/types.ts (2)
  • PaymentRequiredResultV1 (59-79)
  • x402Version (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Lint Packages
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (22)
apps/playground-web/src/app/api/paywall/route.ts (1)

30-32: LGTM!

The header fallback logic correctly prioritizes the v2 PAYMENT-SIGNATURE header while preserving backward compatibility with v1's X-PAYMENT. This aligns well with the broader x402 v2 support being added across the codebase.

apps/portal/src/app/x402/facilitator/page.mdx (1)

106-106: LGTM!

The header fallback order correctly prioritizes the v2 header (PAYMENT-SIGNATURE) while maintaining backward compatibility with v1 (X-PAYMENT).

packages/thirdweb/src/x402/fetchWithPayment.ts (3)

8-11: LGTM!

Clean imports for header utilities and version default. The separation of header logic into a dedicated module improves maintainability.

Also applies to: 20-20


103-103: LGTM!

Defaulting x402Version to defaultX402Version when absent from the response ensures backward compatibility with servers that don't explicitly include the version field.

Also applies to: 121-121


187-201: LGTM!

Dynamic header name resolution based on x402Version is computed once and applied consistently to both the request header and Access-Control-Expose-Headers. This ensures the correct version-specific headers are used throughout the request lifecycle.

apps/portal/src/app/x402/page.mdx (1)

116-116: LGTM!

Documentation correctly updated to prioritize the v2 PAYMENT-SIGNATURE header with fallback to v1 X-PAYMENT.

packages/thirdweb/src/x402/encode.ts (1)

2-5: LGTM!

Import extended appropriately to include the RequestedPaymentRequirements type needed for the new encoding function.

packages/thirdweb/src/x402/settle-payment.ts (4)

4-4: LGTM!

Dynamic header name resolution correctly derives from decodedPayment.x402Version, ensuring the response uses the same header convention as the incoming request.

Also applies to: 158-160


167-175: LGTM!

Success response correctly uses the dynamic header name for both Access-Control-Expose-Headers and the actual payment response header key.


184-184: LGTM!

Error responses correctly preserve the client's x402Version with a fallback to the current protocol version. This ensures consistent version handling across success and error paths.

Also applies to: 200-200


41-43: LGTM!

JSDoc examples updated to reflect the v2 header priority with v1 fallback, consistent with the documentation changes in the portal.

Also applies to: 110-111

packages/thirdweb/src/x402/common.ts (5)

35-54: LGTM! Clean v2 response formatter.

The helper function properly encapsulates v2 header-based format with base64 encoding via encodePaymentRequired. The empty responseBody cast to Record<string, never> correctly represents the v2 format.


59-74: LGTM! Consistent v1 response formatter.

The v1 helper correctly returns a body-based response with Content-Type: application/json and the payment requirements in the body.


91-98: LGTM! Sensible default to v2 format.

Defaulting to v2 format when no payment header is present is appropriate since newer clients should receive the modern header-based format.


104-105: LGTM! Correct version preservation using nullish coalescing.

The ??= operator correctly preserves any client-provided version while defaulting to the current protocol version when absent.


122-136: LGTM! Version-aware error response formatting.

Respecting the client's x402 version when returning error responses ensures clients receive responses in the format they can process.

packages/thirdweb/src/x402/facilitator.ts (2)

13-17: LGTM! Type signature correctly narrowed to V1.

The accepts method return type is appropriately narrowed to PaymentRequiredResultV1 since the API endpoint returns the v1 body-based format. This provides better type safety for consumers.

Also applies to: 55-57


269-307: LGTM! Accepts method correctly includes version in request.

The implementation properly sends x402Version to the server and types the response correctly as V1 format.

packages/thirdweb/src/x402/types.ts (4)

14-16: LGTM! Clean version type derivation.

The as const tuple pattern correctly derives X402Version as a union type 1 | 2, and the default export is set to the current protocol version.


48-49: LGTM! Optional version field with clear documentation.

The optional x402Version field allows clients to specify their preferred version while defaulting to v2 for new integrations.


56-79: LGTM! Comprehensive V1 result type.

The V1 type correctly captures the body-based response format with all necessary error details and optional fields for enhanced error handling.


81-91: LGTM! Clean V2 result type with empty body constraint.

The Record<string, never> type correctly enforces that V2 responses carry payment requirements in headers, not the body.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

packages Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase. SDK Involves changes to the thirdweb SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants